home *** CD-ROM | disk | FTP | other *** search
- #ifndef LISTS_C
- #define LISTS_C
-
- /* Name : list.c
- *
- * Notes: The following passage is taken verbatim from _Amiga_ROM_Kernal
- * _Reference_Manual:_Libraries_3rd_Edition_, page 490
- *
- * One subtlety here must be explained further. The list header is
- * constructed in an efficient, but confusing manner. This of the header
- * as a structure containg the head and tail nodes for the list. The
- * head and tail nodes are placeholders, and never carry data. The head
- * and tail portions of the header actually overlap in memory. lh_Head and
- * lh_Tail form the head node; lh_Tail and lh_TailPred form the tail node.
- * This makes it easy to find the start or end of the list, and eliminates
- * any special cases for insertion or removal.
- *
- * "Head Node" "Tail Node" "Merged Header"
- * +-------------+ +-------------+
- * | ln_Succ | | lh_Head |
- * +-------------+-------------+ +-------------+
- * | ln_Pred = 0 | ln_Succ = 0 | ---> | lh_Tail = 0 |
- * +-------------+-------------+ +-------------+
- * | ln_Pred | | ln_TailPred |
- * +-------------+ +-------------+
- *
- * Figure 23-2: List Header Overlap
- *
- * The lh_Head and lh_Tail fields of the list header act like the
- * ln_Succ and lh_Pred fields of a node. The lh_Tail field is set
- * permanently to NULL, indicating that the head node is indeed the
- * first on the list -- that is, it has no predecessors.
- *
- * Likewise, the lh_Tail and lh_TailPred fields of the list header
- * act like the ln_Succ and ln_Pred fields of a node. Here the NULL
- * lh_Tail indicates that the tail node is indeed the list on the
- * list -- that is, it has no successors.
- *
- * $Log: list.c,v $
- * Revision 2.0 98/07/09 04:57:56 nf
- * Completely rewritten, only the comments left for documentation
- * added FindNode and different faster SortLists
- *
- * Revision 1.4 95/10/26 16:17:42 idr
- * Changed FindName() to use a slightly more optimal loop condition.
- * Changed SortList() to use a radix sort rather than the yucky sort
- * that it was using before. At this time, the old versions of both
- * functions still exist in the code.
- *
- * Revision 1.3 95/10/20 15:03:47 idr
- * Fixed a bug in the Enqueue() function that caused nodes to be added to
- * the list in the wrong order. I also cleaned that function and a couple
- * of others up a little bit so that they're a bit easier to read.
- *
- * Revision 1.2 1995/09/05 02:38:01 idr
- * Move AddHead() and AddTail() so that I would not need to use any
- * bounus function prototypes to prevent compliation errors.
- *
- * Revision 1.1 1995/07/09 20:10:47 idr
- * Initial revision
- */
-
- #include <math.h>
- #include <stdlib.h>
- #include <string.h>
- #include <liblists.h>
-
- /*
- * some list management
- */
-
- /******************************************************************************/
- /* Name : NewList()
- *
- * Notes: The following passage is taken verbatim from _Amiga_ROM_Kernal
- * _Reference_Manual:_Libraries_3rd_Edition_, page 490
- *
- * HEADER INITIALIZATION
- *
- * List headers must be properly initialized before use. It is not
- * adequate to initialize the entire header to zero. The head and tail
- * entries must have specific values. The header must be initialized
- * as follows:
- *
- * 1. Set the lh_Head field to the address of lh_Tail.
- * 2. Clear the lh_Tail field.
- * 3. Set the lh_TailPred field to the address of lh_Head.
- * 4. Set lh_Type to the same data type as the nodes to be kept
- * in the list. (Unless you are using a MinList).
- */
- void nNewList(struct nlist *list)
- {
- list->head = (struct nnode *)&(list->tail);
- list->tail = NULL;
- list->tailpred = (struct nnode *)&(list->head);
- list->nodes = 0;
- }
-
- /******************************************************************************/
- /* Name : AddHead()
- *
- * Notes: This function adds the specified node to the begining (head)
- * of the specified list.
- */
- void nAddHead(struct nlist *list, struct nnode *node)
- {
- /* temporary pointer to a node */
- struct nnode *tempnode;
-
- /* Save the pointer to the old head node. */
- tempnode = list->head;
- /* Make ``node'' the new head. */
- list->head = node;
- /* Link ``node'' to its new neighbors. */
- node->succ = tempnode;
- node->pred = (struct nnode *)list;
- /* Link the old head to the new head. */
- tempnode->pred = node;
- list->nodes++;
- }
-
- /******************************************************************************/
- /* Name : AddTail()
- *
- * Notes: This function adds the specified node to the end (tail) of
- * the given list.
- */
- void nAddTail(struct nlist *list, struct nnode *node)
- {
- struct nnode *tempnode; /* temporary pointer to a node */
- struct nnode *tailnode; /* pointer to the tail part of the list */
-
- /* Get a pointer to the tail part of the list header so that
- * we can just treat it like a normal node.
- */
- tailnode = (struct nnode *)&(list->tail);
- /* Save a pointer to the old tail node. */
- tempnode = tailnode->pred;
- /* Set the new tail node pointer. */
- tailnode->pred = node;
- /* Link the new tail node to its new neighbors. */
- node->pred = tempnode;
- node->succ = tailnode;
- /* Line the old tail node to its new neighbors. */
- tempnode->succ = node;
- list->nodes++;
- }
-
- /******************************************************************************/
- /* Name : RemHead()
- *
- * Notes: This function unlinks and returns the head node in the
- * given list. If the list is empty, this function will
- * return NULL.
- */
- struct nnode *nRemHead(struct nlist *list)
- {
- struct nnode *headnode; /* pointer to the old head of the list */
- struct nnode *tempnode; /* pointer to temporary node */
-
- /* If the head node is the list node in the list, then
- * the list is really empty (see header comment for why
- * this is the case), so we need to return NULL.
- */
- if (list->nodes == 0)
- return (NULL);
- /* Get a pointer to the old head of the list. */
- headnode = list->head;
- /* Get the node after the old head. */
- tempnode = headnode->succ;
- /* Link the header and the node after the old head node
- * to each other.
- */
- list->head = tempnode;
- tempnode->pred = (struct nnode *)list;
- /* Unlink the old head from the list. */
- headnode->succ = headnode->pred = NULL;
- /* Return the old head. */
- list->nodes--;
- return (headnode);
- }
-
- /******************************************************************************/
- /* Name : RemTail()
- *
- * Notes: This function unlinks and returns the tail node in the
- * given list. If the list is empty, this function will
- * return NULL.
- */
- struct nnode *nRemTail(struct nlist *list)
- {
- struct nnode *tailnode; /* pointer to the old tail of the list */
- struct nnode *tempnode; /* pointer to temporary node */
-
- if (list->nodes == 0)
- return (NULL);
- tailnode = list->tailpred;
- tempnode = tailnode->pred;
- list->tailpred = tempnode;
- tempnode->succ = (struct nnode *)&(list->tail);
- list->nodes--;
- return (tailnode);
- }
-
- /******************************************************************************/
- /* Name : Remove()
- *
- * Notes: The following passage is taken verbatim from _Amiga_ROM_Kernal
- * _Reference_Manual:_Libraries_3rd_Edition_, page 492
- *
- * The Remove() function is used to remove a specified node from a list.
- * For example, Remove(node) will remove the specified node from whatever
- * list it is in. To be removed, a node must actually be in a list. If
- * you attempt to remove a node that is not in a list, you will cause
- * serious system problems.
- */
- void nRemove(struct nlist *list, struct nnode *node)
- {
- /* Line the node's neighbors to each other, rather than to node. */
- node->pred->succ = node->succ;
- node->succ->pred = node->pred;
- /* Unlink the node's neighbors from the node. */
- node->succ = node->pred = NULL;
- list->nodes--;
- return;
- }
-
- /******************************************************************************/
- /* Name : Insert()
- *
- * Notes: The following passage is taken verbatim from _Amiga_ROM_Kernal
- * _Reference_Manual:_Libraries_3rd_Edition_, page 492
- *
- * The Insert() function is used for inserting a new node into any
- * position in a list. It always inserts the node following a
- * specified node that is already part of the list. For example,
- * Insert(header, node, pred) inserts the node ``node'' after the node
- * ``pred'' in the specified list. If the pred node points to the list
- * header or is NULL, the new node will be inserted at the head of the
- * list. Similarly, if the pred node points to the lh_Tail of the
- * list, the new node will be inserted at the tail of the list. However,
- * both of these actions can be better accomplished with the functions
- * mentioned in the "Special Case Insertion" section below.
- */
- void nInsert(struct nlist *list, struct nnode *node, struct nnode *pred)
- {
- struct nnode *tempnode; /* temporary pointer to a node */
-
- /* If the pointer to ``pred'' is NULL, then ``node'' will become
- * the head of the list.
- */
- if (pred == NULL) {
- nAddHead(list, node);
- }
- else {
- /* Save the pointer to the node that comes after ``pred''. */
- tempnode = pred->succ;
- /* Link ``pred'' to its new successor. */
- pred->succ = node;
- /* Link ``node'' to its new neighbors. */
- node->pred = pred;
- node->succ = tempnode;
- /* Link pred's old successor back to its new predicessor. */
- tempnode->pred = node;
- list->nodes++;
- }
- }
-
- /******************************************************************************/
- /* Name : preInsert()
- *
- * Notes: The preInsert() function is used for inserting a new node into any
- * position in a list. It always inserts the node preceding a
- * specified node that is already part of the list. For example,
- * Insert(header, node, succ) inserts the node ``node'' before the node
- * ``succ'' in the specified list. If the succ node points to the list
- * header or is NULL, the new node will be inserted at the head of the
- * list. Similarly, if the succ node points to the lh_Tail of the
- * list, the new node will be inserted at the tail of the list.
- * However, both of these actions can be better accomplished with the
- * functions mentioned in the "Special Case Insertion" section below.
- */
- void npreInsert(struct nlist *list, struct nnode *node, struct nnode *succ)
- {
- struct nnode *tempnode; /* temporary pointer to a node */
-
- /* If the pointer to ``succ'' is NULL, then ``node'' will become
- * the head of the list.
- */
- if ((succ == NULL) || (succ->pred == NULL)) {
- nAddHead(list, node);
- }
- else {
- /* Save the pointer to the node that comes before ``succ''. */
- tempnode = succ->pred;
- /* Link ``pred'' to its new Predecessor */
- succ->pred = node;
- /* Link ``node'' to its new neighbors. */
- node->succ = succ;
- node->pred = tempnode;
- /* Link pred's old predecesor back to its new successor. */
- tempnode->succ = node;
- list->nodes++;
- }
- }
-
- /******************************************************************************/
- /* Name : Enqueue()
- *
- * Notes: The following passage is taken verbatim from _Amiga_ROM_Kernal
- * _Reference_Manual:_Libraries_3rd_Edition_, page 492
- *
- * PRIORITIZED INSERTION
- *
- * The list functions discussed so far do not make use of the priority
- * field in a Node. The Enqueue() function is equivalent to Insert(),
- * except that it inserts nodes into a list sorting them according to
- * their priority. It keeps the higher-priority nodes towards the head
- * of the list. All nodes passed to this function must have their
- * priority and name assigned prior to the call. Enqueue(header, mynode)
- * inserts ``mynode'' behind the lowest priority node with a priority
- * greater than or qual to ``mynode's''. For Enqueue() to work properly,
- * the list ``mylist'' already be sorted according to priority. Because the
- * highest priority node is at the head of the list, the RemHead() function
- * will remove the highest priority node. Likewise, RemTail() will remove
- * the lowest priority node.
- *
- * FIFO Is Used For The Same Priority. If you add a node that has
- * the same priority as another node in the queue, Enqueue() will
- * use FIFO ordering. The new node is inserted following the list
- * node of equal priority.
- */
- struct nnode *nFindNode(struct nlist *list, int data)
- {
- register struct nnode *currnode = list->head;
- register int middle;
-
- if ((middle = list->nodes - 1) > 0) {
- register int i;
-
- for (i = middle; i >= 0; i--) { /* real tries to find node is ln2(rgbnodes) */
- /* find the middle */
- register int shift = middle >> 1;
- register int loop = shift;
- register struct nnode *middnode = currnode;
-
- if (!loop)
- break;
- else { /* compare */
- while ((loop--) > 0) /* this eats most of the time */
- middnode = middnode->succ;
-
- if (data < middnode->data) { /* if less than jump */
- currnode = middnode;
- middle = middle - shift;
- }
- else if (data > middnode->data) /* if greater than stay */
- middle = shift;
- else
- break;
- }
- }
- }
- else {
- if (middle < 0)
- currnode = 0;
- }
-
- return currnode;
- }
-
- void nEnqueue(struct nlist *list, struct nnode *node)
- {
- int data = node->data;
-
- /* special case for empty list element */
- if (list->nodes = 0)
- nAddTail(list, node);
- /* special case for first list element */
- else if (data >= list->head->data)
- nAddHead(list, node);
- /* special case for last list element */
- else if (data <= list->tailpred->data)
- nAddTail(list, node);
- /* we must walk the tree */
- else
- nInsert(list, node, nFindNode(list, data));
- }
-
- /******************************************************************************/
- /* Name : GetHead()
- *
- * Notes: This function is very similar in concept to RemHead(), with the
- * exception that it doesn't modify the linked list.
- */
- struct nnode *nGetHead(struct nlist *list)
- {
- return ((list->head->succ == NULL) ? NULL : list->head);
- }
-
- /******************************************************************************/
- /* Name : GetTail()
- *
- * Notes: This function is very similar in concept to RemTail(), with the
- * exception that it doesn't modify the linked list.
- */
- struct nnode *nGetTail(struct nlist *list)
- {
- return ((list->tailpred->pred == NULL) ? NULL : list->tailpred);
- }
-
- /******************************************************************************/
- /* Name : SuccNode()
- *
- * Notes: This function returns a pointer to the next node, if one exists.
- *
- */
- struct nnode *nSuccNode(struct nnode *node)
- {
- return ((node->succ->succ == NULL) ? NULL : node->succ);
- }
-
- /******************************************************************************/
- /* Name : MoveList()
- *
- * Notes: This function is used to take the nodes that are in one list and
- * put them in another list. It is important to note, that the
- * source list is NOT VALID AFTER THIS CALL. Also, any nodes that
- * might already be in the destination list will be LOST.
- */
- void nMoveList(struct nlist *slist, struct nlist *dlist)
- {
- struct nnode *node;
-
- /* First, make the destination list point to its new head and tail
- * nodes. Also, init. the lh_Tail value.
- */
- dlist->head = slist->head;
- dlist->tail = NULL;
- dlist->tailpred = slist->tailpred;
- dlist->nodes = slist->nodes;
- /* Now, make the new tail node point to the list header. */
- node = dlist->tailpred;
- node->succ = (struct nnode *)&(dlist->tail);
- /* Now, make the new head node point back at the list header. */
- node = dlist->head;
- node->pred = (struct nnode *)&(dlist->head);
- }
-
- /******************************************************************************/
- /* Name : MoveNode()
- *
- */
- void nMoveNode(struct nlist *slist, struct nlist *dlist, struct nnode *node)
- {
- nRemove(slist, node);
- nEnqueue(dlist, node);
- }
-
- /******************************************************************************/
- /* Name : AppendList()
- *
- * Notes: This function takes pointer to two list header, preList and
- * postList, and creates a list that is a combination of the
- * two, with the nodes in preList appearing first, followed by
- * the nodes of postList. The resultant list is linked with the
- * header specified by preList.
- */
- void nAppendList(struct nlist *prelist, struct nlist *postlist)
- {
- struct nnode *lastOfPre = prelist->tailpred;
- struct nnode *firstOfPost = postlist->head;
-
- struct nnode *lastOfPost;
-
- /* If the list that we were going to append to is empty, then the
- * resultant list is just the list that we were going to append.
- * The easy way to do this is to just copy the postlist to the
- * prelist header.
- */
- if (prelist->nodes == 0) {
- nMoveList(postlist, prelist);
- nNewList(postlist);
- }
- /* If the list that we were going to append is empty, then we don't
- * really have a chore to do. Let's return to the caller now.
- */
- else if (postlist->nodes != 0) {
- /* This is the point where this routine gets more than just a
- * little complex. This would make a good assignment for 3rd term,
- * first year CS students. It would weed the wimps out from the
- * big dogs. :)
- *
- * There are actually two steps to this process. The first step is
- * to link the node at the end of the prelist to the node at the
- * start of the postlist. The second step is to link the node at
- * the end of the postlist to the tail part of the list header.
- */
- lastOfPre->succ = firstOfPost;
- firstOfPost->pred = lastOfPre;
- lastOfPost = postlist->tailpred;
- lastOfPost->succ = (struct nnode *)&(prelist->tail);
- prelist->tailpred = lastOfPost;
- prelist->nodes += postlist->nodes;
- /* The last step is to make sure the user won't do something totally
- * retarded and use the postlist. We'll do that by clearing the
- * list out.
- */
- nNewList(postlist);
- }
- }
-
- /******************************************************************************/
- /* Name : PrependList()
- *
- * Notes: This function takes pointer to two list header, preList and
- * postList, and creates a list that is a combination of the
- * two, with the nodes in preList appearing first, followed by
- * the nodes of postList. The resultant list is linked with the
- * header specified by postList.
- */
- void nPrependList(struct nlist *postList, struct nlist *preList)
- {
- /* This is kind of a cheesy way to do this, but it works. :) The
- * main reason that I did this routine this way is that it is a
- * LOT less code, and I don't think that PrependList() will, in
- * general, be used as much as AppendList().
- */
- nAppendList(preList, postList);
- nMoveList(preList, postList);
- nNewList(preList);
- }
-
- /******************************************************************************/
- /* Name : SortList()
- *
- * Notes: This function is used to accomplish a slow and sleazy sorting of a
- * list by copying the list into a temporary list and sorting each
- * member as it gets copied. Smaller Node Priorities get sorted to the
- * bottom.
- */
- #define RADIX 32
- #define RADIX_DIVIDE (1<<7) /* special case for -128 upto 127 */
- #define RADIX_OFFSET (RADIX/2) /* special case for -128 upto 127 */
-
- /*
- * the list contains data as follows:
- * es existieren mehr werte fuer niedrige zahlen und weniger werte fuer hohe zahlen:
- * 100 werte im bereich 0-100
- * 50 werte im bereich 100-200
- * 25 werte im bereich 200-300
- * der groeszte wert ist nicht berechenbar und liegt in irgendeinem bereich
- */
- void nSortListWeighted(struct nlist *list, int radix)
- {
- struct nlist **bucket; /* Sorting buckets. */
- struct nnode *node = (struct nnode *)list; /* Temporary list node pointer. */
- int lcv; /* Local counter variable. */
- int radix_divide = 0; /* the greatest data in a node */
-
- /* allocate number of lists
- */
- if ((bucket = malloc(radix * sizeof(struct nlist *))) == 0)
- return;
-
- for (lcv = radix - 1; lcv >= 0; lcv--)
- if ((bucket[lcv] = malloc(sizeof(struct nlist))) == 0)
- return;
-
- /* get the highest data-value
- */
- for (lcv = list->nodes; lcv > 0; lcv--) { /* nodes !!! */
- node = node->succ;
- if (node->data > radix_divide)
- radix_divide = node->data;
- }
- radix_divide = sqrt(radix_divide) / radix; /* the worth */
- radix_divide++;
-
- /* First, initialize all of the buckets that we will be sorting the
- * nodes into.
- */
- for (lcv = radix - 1; lcv >= 0; lcv--)
- nNewList(bucket[lcv]);
-
- /* Now, move every node out of the source list into the bucked that
- * it belongs in.
- */
- node = list->head; /* Temporary list node pointer. */
- for (lcv = list->nodes - 1; lcv >= 0; lcv--) {
- register struct nnode *succnode = node->succ;
- register int radix2 = sqrt(node->data) / radix_divide;
-
- nEnqueue(bucket[radix2], node);
- node = succnode;
- }
-
- /* Now merge all of the buckets together to form the final, sorted
- * linked list.
- */
- nMoveList(bucket[radix - 1], list);
- for (lcv = radix - 2; lcv >= 0; lcv--)
- nAppendList(list, bucket[lcv]);
-
- for (lcv = radix - 1; lcv >= 0; lcv--)
- free(bucket[lcv]);
- free(bucket);
- }
-
- /*
- * the list contains data as follows:
- * die werte sind linear im daten-bereich verteilt
- * der groeszte wert ist nicht berechenbar und liegt in irgendeinem bereich
- */
- void nSortListLinear(struct nlist *list, int radix)
- {
- struct nlist **bucket; /* Sorting buckets. */
- struct nnode *node = (struct nnode *)list; /* Temporary list node pointer. */
- int lcv; /* Local counter variable. */
- int radix_divide = 0; /* the greatest data in a node */
-
- /* allocate number of lists
- */
- if ((bucket = malloc(radix * sizeof(struct nlist *))) == 0)
- return;
-
- for (lcv = radix - 1; lcv >= 0; lcv--)
- if ((bucket[lcv] = malloc(sizeof(struct nlist))) == 0)
- return;
-
- /* get the highest data-value
- */
- for (lcv = list->nodes; lcv > 0; lcv--) { /* nodes !!! */
- node = node->succ;
- if (node->data > radix_divide)
- radix_divide = node->data;
- }
- radix_divide = radix_divide / radix; /* the worth */
- radix_divide++;
-
- /* First, initialize all of the buckets that we will be sorting the
- * nodes into.
- */
- for (lcv = radix - 1; lcv >= 0; lcv--)
- nNewList(bucket[lcv]);
-
- /* Now, move every node out of the source list into the bucked that
- * it belongs in.
- */
- node = list->head; /* Temporary list node pointer. */
- for (lcv = list->nodes - 1; lcv >= 0; lcv--) {
- register struct nnode *succnode = node->succ;
- register int radix2 = node->data / radix_divide;
-
- nEnqueue(bucket[radix2], node);
- node = succnode;
- }
-
- /* Now merge all of the buckets together to form the final, sorted
- * linked list.
- */
- nMoveList(bucket[radix - 1], list);
- for (lcv = radix - 2; lcv >= 0; lcv--)
- nAppendList(list, bucket[lcv]);
-
- for (lcv = radix - 1; lcv >= 0; lcv--)
- free(bucket[lcv]);
- free(bucket);
- }
-
- /*
- * the list contains data as follows:
- * die werte sind linear im daten-bereich verteilt
- * der groeszte wert ist berechenbar und wird angegeben
- */
- void nSortListLinearMax(struct nlist *list, int radix, int radix_divide)
- {
- struct nlist **bucket; /* Sorting buckets. */
- struct nnode *node = (struct nnode *)list; /* Temporary list node pointer. */
- int lcv; /* Local counter variable. */
-
- /* allocate number of lists
- */
- if ((bucket = malloc(radix * sizeof(struct nlist *))) == 0)
- return;
-
- for (lcv = radix - 1; lcv >= 0; lcv--)
- if ((bucket[lcv] = malloc(sizeof(struct nlist))) == 0)
- return;
-
- /* get the highest data-value
- */
- radix_divide = radix_divide / radix; /* the worth */
- radix_divide++;
-
- /* First, initialize all of the buckets that we will be sorting the
- * nodes into.
- */
- for (lcv = radix - 1; lcv >= 0; lcv--)
- nNewList(bucket[lcv]);
-
- /* Now, move every node out of the source list into the bucked that
- * it belongs in.
- */
- node = list->head; /* Temporary list node pointer. */
- for (lcv = list->nodes - 1; lcv >= 0; lcv--) {
- register struct nnode *succnode = node->succ;
- register int radix2 = node->data / radix_divide;
-
- nEnqueue(bucket[radix2], node);
- node = succnode;
- }
-
- /* Now merge all of the buckets together to form the final, sorted
- * linked list.
- */
- nMoveList(bucket[radix - 1], list);
- for (lcv = radix - 2; lcv >= 0; lcv--)
- nAppendList(list, bucket[lcv]);
-
- for (lcv = radix - 1; lcv >= 0; lcv--)
- free(bucket[lcv]);
- free(bucket);
- }
-
- #endif /* LISTS_C */
-